import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { JwtService } from "@nestjs/jwt";
import session from "express-session";

import { AppUserType } from "@/core/auth/constants";
import { CONFIG_KEY } from "@/core/config/configurations";
import { PrismaService } from "@/core/db/prisma.service";
import BusinessException from "@/core/exception/BusinessException";
import { BcryptUtils } from "@/core/utils/BcryptUtils";
import { CaptchaUtils } from "@/core/utils/CaptchaUtils";
import { JwtPayloadVO } from "@/core/vo/jwt-payload.vo";

import { LoginVo } from "./vo/login.vo";

@Injectable()
export class LoginService {
    constructor(
        private readonly prismaService: PrismaService,
        private readonly jwtService: JwtService,
        private readonly configService: ConfigService
    ) {}

    private checkCaptcha(captcha?: string, session?: session.Session & Partial<session.SessionData>) {
        // 清除过期验证码
        const clearCaptcha = () => {
            delete session.captcha;
            session.save(err => {
                if (err) new Error("Session save error");
            });
        };

        if (!captcha || !session.captcha || !session.captcha.text || !session.captcha.expiresAt) {
            throw new BusinessException("CAPTCHA_ERROR"); // 验证码不存在或格式不正确
        }

        // 检查验证码是否已过期
        if (Date.now() > session.captcha.expiresAt) {
            // 清除过期验证码
            clearCaptcha();
            throw new BusinessException("CAPTCHA_EXPIRED");
        }

        if (captcha !== session.captcha.text) {
            // 清除验证码
            clearCaptcha();
            throw new BusinessException("CAPTCHA_ERROR");
        }
        // 如果验证码有效，也要清除它以防重复使用
        clearCaptcha();
    }

    async login(
        username: string,
        password: string,
        ip: string,
        userAgent: string,
        captcha?: string,
        session?: session.Session & Partial<session.SessionData>
    ) {
        const { enabled } = this.configService.get(CONFIG_KEY.captcha);
        if (enabled) this.checkCaptcha(captcha, session);

        const user = await this.prismaService.sysUser.findUnique({
            where: {
                username,
                isDeleted: false
            }
        });

        if (!user) throw new BusinessException("USER_NOT_FOUND");

        if (user.isDisabled) throw new BusinessException("ACCOUNT_LOCKED");

        const isMatch = await BcryptUtils.comparePassword(password, user.password);
        if (!isMatch) throw new BusinessException("PASSWORD_EXPIRED");

        await this.prismaService.sysUser.update({
            where: {
                id: user.id
            },
            data: {
                lastLoginIp: ip,
                lastLoginAt: new Date()
            }
        });

        // 记录登录日志
        // this.recordLoginLog(user.id, username, ip, userAgent);

        // sign 方法要求传入一个纯对象，不能是类实例，所以要解构一下
        const jwt = this.jwtService.sign({
            ...new JwtPayloadVO(user.id, username, AppUserType.ADMIN_USER)
        });

        return new LoginVo(user, jwt);
    }

    /**
     * 记录登录日志
     * @param userId
     * @param username
     * @param ip
     * @param userAgent
     */
    // private async recordLoginLog(userId: number, username: string, ip: string, userAgent: string) {
    //     // ::1 为 ipv6 下的本地 IP。这里转换成 ipv4 的本地IP：127.0.0.1，因为关闭了 Ip2Region 的 ipv6 查询功能
    //     if (ip === "::1") ip = "127.0.0.1";
    //     const region = await Ip2RegionUtils.getIpRegionInfo(ip);

    //     const { os, browser, platform } = UserAgentUtils.parse(userAgent);

    //     await this.prismaService.sysLoginLog.create({
    //         data: {
    //             userId,
    //             username,
    //             os: os.name,
    //             browser: browser.name,
    //             platform: platform.type,
    //             ip,
    //             address: region ? Object.values(region).join("-") : ""
    //         }
    //     });
    // }

    async register(username: string, password: string, email: string) {
        const user = await this.prismaService.sysUser.findFirst({
            where: {
                username
            }
        });

        if (user) throw new BusinessException("USER_EXIST");

        const hashedPassword = await BcryptUtils.hashPassword(password);

        await this.prismaService.sysUser.create({
            data: {
                username,
                nickname: username,
                email,
                password: hashedPassword,
                // avatar: `https://api.dicebear.com/9.x/lorelei/svg?seed=${Date.now()}`
                avatar: `http://localhost:7001/image/admin-avatar.jpeg`
            }
        });
    }
    logout(id: number) {
        return id;
    }
    getCaptcha(session: session.Session & Partial<session.SessionData>) {
        const { expired } = this.configService.get(CONFIG_KEY.captcha);
        const captcha = CaptchaUtils.generateCaptcha();
        session.captcha = {
            text: captcha.text,
            expiresAt: Date.now() + expired // 以毫秒为单位
        };
        // 确保会话数据被保存
        session.save(err => {
            // TODO: 应该用 winston 来记录错误日志
            if (err) new Error("Session save error");
        });

        return captcha.data;
    }
}
